//////////////////////////////////////////////////////////////////////////////
//       Copyright (c) 2003 - RealTime Gaming.  All rights reserved.        //
//////////////////////////////////////////////////////////////////////////////

#include <malloc.h>
#include <memory.h>
#include <wchar.h>
#include <tchar.h>
#include <windows.h>
#include <stdarg.h>
#include <stdio.h>

#include "ustring.h"
using namespace Library;


// Initial stack allocated buffer measured in characters, not bytes, which
// String::sprintf() uses as a temporary buffer.  Must be a power of two (2).
#define USTRING_SPRINTF_MIN		(1024)

// Maximum heap allocated buffer measured in characters, not bytes, which
// String::sprintf() uses as a temporary buffer.  Must be a power of two (2).
#define USTRING_SPRINTF_MAX		(1024 * 1024)

const String String::emptyString = "";

//****************************************************************************
// String Class
//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
/// Default Constructor
//////////////////////////////////////////////////////////////////////////////
String::String()
{
	m_pData			= 0;
	m_nAllocated	= 0;
	m_nLength		= 0;
	m_bLocked		= false;
	m_pChar			= 0;
}

//////////////////////////////////////////////////////////////////////////////
/// Copy constructor
//////////////////////////////////////////////////////////////////////////////
String::String(const String & str)
{
	m_pData			= 0;
	m_nAllocated	= 0;
	m_nLength		= 0;
	m_bLocked		= false;
	m_pChar			= 0;

	copyString(str, str.m_nLength);
}

//////////////////////////////////////////////////////////////////////////////
/// Construct from a ANSI string literal
//////////////////////////////////////////////////////////////////////////////
String::String(const char * szString)
{
	m_pData			= 0;
	m_nAllocated	= 0;
	m_nLength		= 0;
	m_bLocked		= false;
	m_pChar			= 0;

	copyString(szString);
}

//////////////////////////////////////////////////////////////////////////////
/// Construct from a unicode string literal
//////////////////////////////////////////////////////////////////////////////
String::String(const uint16 * szString)
{
	m_pData			= 0;
	m_nAllocated	= 0;
	m_nLength		= 0;
	m_bLocked		= false;
	m_pChar			= 0;

	copyString(szString);
}

//////////////////////////////////////////////////////////////////////////////
/// Construct from raw data pointer, no terminating NULL.
//////////////////////////////////////////////////////////////////////////////
String::String( uint16* pRawData, uint32 nSizeInBytes )
{
	m_pData			= 0;
	m_nAllocated	= 0;
	m_nLength		= 0;
	m_bLocked		= false;
	m_pChar			= 0;

	ASSERT(pRawData);

	if (pRawData && nSizeInBytes)
	{
		// This method takes a UCS2 "raw" pointer, so we assume it's 2
		//  bytes per character.
		ASSERT( (nSizeInBytes % 2) == 0 );

		// Convert bytes
		m_nLength = nSizeInBytes / sizeof( uint16 );

		// Add space for a terminating null
		m_nAllocated = m_nLength + 1;

		// Allocate the memory
		m_pData = new uint16[ m_nAllocated ];

		if (m_pData)
		{
			wcsncpy( m_pData, pRawData, m_nLength );
			m_pData[m_nLength] = Character::null;
			m_pChar = m_pData;
		}
		else
		{
			m_nAllocated	= 0;
			m_nLength		= 0;
		}
	}
}

//////////////////////////////////////////////////////////////////////////////
/// Construct from a single character
//////////////////////////////////////////////////////////////////////////////
String::String(const String::Character & ch)
{
	uint16 str[2] = { ch, '\0' };

	m_pData			= 0;
	m_nAllocated	= 0;
	m_nLength		= 0;
	m_bLocked		= false;
	m_pChar			= 0;

	copyString( str, 1 );
}

//////////////////////////////////////////////////////////////////////////////
/// Destructor
//////////////////////////////////////////////////////////////////////////////
String::~String()
{
	// Just a sanity check.
	ASSERT( !m_pData || (m_pData && m_nAllocated) );

	// Yet another sanity check!
	ASSERT( !m_pData || ( m_pData && m_nLength == wcslen(m_pData)) );

	// Delete the actual data
	delete [] m_pData;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				A const pointer the actual string data.
///
/// This is the cast operator to a string pointer.  This cast allows us to
/// use the array operator as an R-Value.  For example, uint16 c = s[1];
//////////////////////////////////////////////////////////////////////////////
String::operator const uint16*() const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

	if (!m_pData)
		return emptyString;

	return m_pData;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				A reference to this object, to allow chaining of
///						assignment.
///
/// @param sString		Value to be assigned.
///
/// This is the assignment operator that takes String object.
//////////////////////////////////////////////////////////////////////////////
const String&
String::operator=( const String& sString )
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return *this;
    }

	// First, check for assignement to self!!!
	if ( this != &sString )
	{
		// Copy the string
		copyString( sString );
	}

	// return a ref to ourselves
	return *this;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				A reference to this object, to allow chaining of
///						assignment.
///
/// @param sString		String to be concatenated onto the end of this one.
///
/// This is the concatenation operator.  This will
/// work with other String objects because we have defined a cast operator
/// that can cast a String to a string pointer.
//////////////////////////////////////////////////////////////////////////////
const String&
String::operator+=( const String& sString )
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return *this;
    }

	// Only do work if we have to
	if ( !sString.isEmpty() )
	{
		// Calculate the size of the buffer required to hold the new string
		uint32 nBuffSize = m_nLength + sString.m_nLength + 1;

		// If we already have that much space, just concatenate it
		if ( nBuffSize <= m_nAllocated )
		{
			concatenateString( sString );
		}
		else
		{
            // We are going to grow the string
            nBuffSize = MAX( m_nLength * 2 + 1, nBuffSize);

			// Allocate the temp buffer and start using it
			uint16* pTemp = new uint16[ nBuffSize ];
			
			if (m_pData)
			{
				wcscpy( pTemp, m_pData );
				delete [] m_pData;
			}
			else
			{
				pTemp[0] = Character::null;
			}
			m_pData = pTemp;
			m_nAllocated = nBuffSize;
			m_pChar = m_pData;

            // Concatenate the strings
			concatenateString( sString );
		}
	}

	// return a ref to ourselves
	return *this;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				A copy of the newly created string that is a result
///                       of the "+" operator.
///
/// @param sString		String to be concatenated onto the end of this one.
///
/// This is a concatenation operator that returns a new string that is the
///  result of concatening two others.
//////////////////////////////////////////////////////////////////////////////
const String
String::operator+( const String& sString ) const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return *this;
    }

    String sReturn = *this;
    sReturn += sString;
    return sReturn;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				True if strings are equal.
///
/// @param sString		String to compare.
///
/// The comparison operator, takes a String object.
//////////////////////////////////////////////////////////////////////////////
bool8
String::operator==(const String& sString) const
{
	return (bool8)(compare(0, NPOS, (const uint16*)sString) == 0);
}

//////////////////////////////////////////////////////////////////////////////
/// @return				True if strings are equal.
///
/// @param szString		Character pointer to null terminated string to
///                       compare to.
///
/// The comparison operator, takes a character pointer.  This method is used
///   for effeciency so that a conversion do a String obect does not have
///   to take place whenver a comparison is done.
//////////////////////////////////////////////////////////////////////////////
bool8
String::operator==( const uint16* szString ) const
{
	return (bool8)(compare(0, NPOS, (const uint16*)szString) == 0);
}

//////////////////////////////////////////////////////////////////////////////
/// @return				True if strings are equal.
///
/// @param sString		String to compare.
///
/// The comparison operator, takes a String object.
//////////////////////////////////////////////////////////////////////////////
bool8
String::operator!=( const String& sString ) const
{
	return (bool8)(compare(0, NPOS, (const uint16*)sString) != 0);
}

//////////////////////////////////////////////////////////////////////////////
/// @return    The length of the string, in characters (not including the
///            terminating null).
///
/// This method returns the length of the string in characters and does not
/// include the terminating null character.
//////////////////////////////////////////////////////////////////////////////
uint32
String::getLength() const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return (uint32)NPOS;
    }

	return m_nLength;
}

//////////////////////////////////////////////////////////////////////////////
/// @return    True for empty string.
///
/// This method returns true if the string is empty.
//////////////////////////////////////////////////////////////////////////////
bool8
String::isEmpty() const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return false;
    }

    ASSERT( !m_pData || m_nLength == wcslen(m_pData) );

	return( m_nLength == 0 );
}

//////////////////////////////////////////////////////////////////////////////
/// This method empties and clears the current string.
//////////////////////////////////////////////////////////////////////////////
void
String::empty()
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if (m_bLocked)
    {
        ASSERT(false);
        return;
    }

	m_nLength = 0;
	if (m_pData)
		m_pData[0] = Character::null;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				Index of the search string in the current string,
///                     if that string is not found, will return String::NPOS.
///
/// @param sString		String to search for.
///
/// This method will search for the first occurance of the "search" string
/// and return the index of that string.
//////////////////////////////////////////////////////////////////////////////
uint32
String::find(const String & sString, const uint32 sIndex) const
{
	uint32 nRetVal = (uint32)NPOS;

	// Only look if we have something to find!
	if (!isEmpty() && !sString.isEmpty())
	{
		uint16 * p = wcsstr(m_pData + sIndex, sString);

		if (p)
			nRetVal = p - m_pData;
	}

	return nRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				Index of the search string in the current string,
///                     if that string is not found, will return String::NPOS.
///
/// @param sString		String to search for.
///
/// This method will search for the first occurance of the "search" string
/// and return the index of that string.
//////////////////////////////////////////////////////////////////////////////
uint32
String::find(const String::Character & sCharacter, const uint32 sIndex) const
{
	return find(String(sCharacter), sIndex);
}

//////////////////////////////////////////////////////////////////////////////
/// @return				Index of the search character in the current string,
///                     if that character is not found, will return
///                       String::NPOS.
///
/// @param sString		String to search for.
///
/// This method will search for the first occurance of the "search" character
/// and return the index of that character, starting from the right of the
/// current string and moving to the left.
//////////////////////////////////////////////////////////////////////////////
uint32
String::reverseFind(const String & sString, const uint32 sIndex) const
{
	uint32 nRetVal = (uint32)NPOS;

	String reversed = *this;
	String reversedSearch = sString;

	_wcsrev(reversed.m_pData);
	_wcsrev(reversedSearch.m_pData);

	nRetVal = reversed.find(reversedSearch, sIndex);

	if (nRetVal != NPOS)
		nRetVal = getLength() - nRetVal - 1;

	return nRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				Index of the search character in the current string,
///                     if that character is not found, will return
///                       String::NPOS.
///
/// @param char		    Character to search for.
///
/// This method will search for the first occurance of the "search" character
/// and return the index of that character, starting from the right of the
/// current string and moving to the left.
//////////////////////////////////////////////////////////////////////////////
uint32
String::reverseFind( const Character& ch, const uint32 sIndex ) const
{
	int32 nRetVal = getLength() - 1;

    while ( (nRetVal > -1) && m_pData[nRetVal] != ch )
    {
		nRetVal--;
    }

    // Create proper return condition
    if ( nRetVal == -1 ) nRetVal = NPOS;

	return nRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @param sSearch		String to search for.
///
/// @param sReplace		Replacement string.
///
/// This method will search for all occurances of the "search" string and
/// replace each occurance with the "replace" string.
//////////////////////////////////////////////////////////////////////////////
void
String::replace(const String & sSearch, const String & sReplace)
{
	uint16 *	src;
	uint16 *	dst;
	uint16 *	temp;
	uint32		length;

    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if (m_bLocked)
    {
        ASSERT(false);
        return;
    }

	// If we are empty or nothing to search for, return
	if (isEmpty() || sSearch.isEmpty())
		return;

	// Allocate enough memory for a worst case scenario (every individual
	// character in this string is replaced by the replace string).
	// NOTE: The replace string could be empty, so we'll take the larger
	//       of the two values below.
	length = MAX(getLength() + 1, getLength() * sReplace.getLength() + 1);
	temp = new uint16[ length ];
	if (temp)
	{
		src		= m_pData;
		dst		= temp;
		length	= sSearch.getLength();

		while (src && *src)
		{
			if (wcsncmp(src, sSearch, length) == 0)
			{
				const uint16 * src_replace = sReplace;

				while (src_replace && *src_replace)
					*dst++ = *src_replace++;

				src += length;
			}
			else
				*dst++ = *src++;
		}

		*dst = '\0';

		copyString(temp);

		delete [] temp;
	}
}

//////////////////////////////////////////////////////////////////////////////
/// Convert string to lower case.
//////////////////////////////////////////////////////////////////////////////
void
String::toLower()
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return;
    }

	if (m_pData)
		wcslwr( m_pData );
}

//////////////////////////////////////////////////////////////////////////////
/// Convert string to upper case.
//////////////////////////////////////////////////////////////////////////////
void
String::toUpper()
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return;
    }
	
	if (m_pData)
		_wcsupr( m_pData );
}

//////////////////////////////////////////////////////////////////////////////
/// @return   Unsigned integer value from the string.
///
/// Converts the string to an unsigned integer.
//////////////////////////////////////////////////////////////////////////////
uint32
String::toUInt32(int32 base) const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

	const wchar_t* str = *this;
	wchar_t* end = 0;
	int32 number = 0;

	number = wcstol(str, &end, base);
	if (str == end || *end)
		return 0;

    return number;
}

//////////////////////////////////////////////////////////////////////////////
/// @return     Integer value from the string.
///
/// Converts the string to an integer.
//////////////////////////////////////////////////////////////////////////////
int32
String::toInt32(int32 base) const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

	const wchar_t* str = *this;
	wchar_t* end = 0;
	int32 number = 0;

	number = wcstol(str, &end, base);
	if (str == end || *end)
		return 0;

    return number;
}

//////////////////////////////////////////////////////////////////////////////
/// @return   Unsigned integer value from the string.
///
/// Converts the string to an unsigned integer.
//////////////////////////////////////////////////////////////////////////////
uint16
String::toUInt16(int32 base) const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

	const wchar_t* str = *this;
	wchar_t* end = 0;
	int32 number = 0;

	number = wcstol(str, &end, base);
	if (str == end || *end)
		return 0;

    return (uint16)number;
}

//////////////////////////////////////////////////////////////////////////////
/// @return         Copy of a string containing the characters requested.
///
/// @param index    Starting index of characters to begin extracting.  If this
///                 index exceeds the length of the string, an empty string
///                 will be returned.
///
/// @param length   Number of characters to extract.  This length will be
///                 reduced should it exceed the number of characters
///                 available.
///
/// Extracts a set of characters from the string and returns them as a
///  new string.
//////////////////////////////////////////////////////////////////////////////
String
String::substring(uint32 index, uint32 length) const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if (m_bLocked)
    {
        ASSERT(false);
        return String("");
    }

	if (index < getLength())
	{
		length = MIN(getLength() - index, length);
		return String(m_pData + index, length * sizeof(uint16));
	}
	else
		return String("");
}

//////////////////////////////////////////////////////////////////////////////
// Trim left whitespace
//////////////////////////////////////////////////////////////////////////////
void
String::trimLeft()
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if (m_bLocked)
    {
        ASSERT(false);
        return;
    }

	if (!m_pData)
		return;

	uint16 * p    = m_pData;
	uint16 * pEnd = m_pData + getLength();

	// Skip the whitespace.
	while (iswspace(*p) && p < pEnd)
		p++;

	if (p != m_pData)
	{
		// Shift the string to the left (along with the terminating null).
		m_nLength = pEnd - p;
		memmove(m_pData, p, (m_nLength + 1) * sizeof(uint16));
	}
}

//////////////////////////////////////////////////////////////////////////////
// Time right whitespace
//////////////////////////////////////////////////////////////////////////////
void
String::trimRight()
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return;
    }

	if (!m_pData)
		return;

	uint16 * p      = m_pData + getLength();
    uint16 * pStart = m_pData;

	while (--p >= pStart && iswspace(*p))
		/* Do nothing. */;

    m_nLength = ++p - pStart;
	*p = Character::null;
}

//////////////////////////////////////////////////////////////////////////////
// Trim both left and right whitespace.
//////////////////////////////////////////////////////////////////////////////
void
String::trim()
{
    trimLeft();
    trimRight();
}

//////////////////////////////////////////////////////////////////////////////
/// @param index	Index in string to set to a new character.
///
/// @param ch       New character to set at the given index.
///
/// This method will set a character in a string at a given index to the
///   new value specified.  You may use Character::null here.
//////////////////////////////////////////////////////////////////////////////
void
String::setChar( uint32 index, Character ch )
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return;
    }

	if ( index < m_nLength )
    {
        m_pData[index] = ch;

        // Recalc the length if we need to
        if ( ch == Character::null )
            m_nLength = index;
    }
    else
    {
        // passed in invalid index!!!
        ASSERT(false);
    }
}

//////////////////////////////////////////////////////////////////////////////
/// @param format	"printf like" format specification.
///
/// @param ...		Variable list of arguments in "printf like" format.
///
/// This method will search for all occurances of the "search" string and
/// replace each occurance with the "replace" string.
//////////////////////////////////////////////////////////////////////////////
int32
String::sprintf(const char * format, ...)
{
    va_list		args;
	char *		buffer;
	int32		size;
	int32		count;

    // If GetBuffer() was called, don't allow operations until
	//   ReleaseBuffer() is called.
    if (m_bLocked)
    {
        ASSERT(false);
        return -1;
    }

	va_start(args, format);

	size	= USTRING_SPRINTF_MIN;
	buffer	= (char *)_alloca(size * sizeof(*buffer));
	count	= _vsnprintf(buffer, size, format, args);

	if (count >= 0)
        copyString(buffer);
	else
	{
		for (buffer = 0; size <= USTRING_SPRINTF_MAX; /* empty */)
		{
			size	= size << 1;
			buffer	= (char *)malloc(size * sizeof(*buffer));
			if (!buffer)
				break;

			count = _vsnprintf(buffer, size, format, args);
			if (count >= 0)
				break;

			free(buffer);
			buffer = 0;
		}

		if (buffer)
		{
			copyString(buffer);
			free(buffer);
		}
	}

    va_end(args);

	return count;
}

//////////////////////////////////////////////////////////////////////////////
/// @param format	"printf like" format specification.
///
/// @param ...		Variable list of arguments in "printf like" format.
///
/// This method will search for all occurances of the "search" string and
/// replace each occurance with the "replace" string.
//////////////////////////////////////////////////////////////////////////////
int32
String::sprintf(const uint16* format, ...)
{
    va_list		args;
	uint16 *	buffer;
	int32		size;
	int32		count;

    // If GetBuffer() was called, don't allow operations until
	//   ReleaseBuffer() is called.
    if (m_bLocked)
    {
        ASSERT(false);
        return -1;
    }

	va_start(args, format);

	size	= USTRING_SPRINTF_MIN;
	buffer	= (uint16 *)_alloca(size * sizeof(*buffer));
    count	= _vsnwprintf(buffer, size, format, args );

	if (count >= 0)
        copyString(buffer);
	else
	{
		for (buffer = 0; size <= USTRING_SPRINTF_MAX; /* empty */)
		{
			size	= size << 1;
			buffer	= (uint16 *)malloc(size * sizeof(*buffer));
			if (!buffer)
				break;

			count = _vsnwprintf(buffer, size, format, args );
			if (count >= 0)
				break;

			free(buffer);
			buffer = 0;
		}

		if (buffer)
		{
			copyString(buffer);
			free(buffer);
		}
	}

    va_end(args);

	return count;
}

int32
String::caseCompare(int32 start, int32 length, const uint16 * aString) const
{
	if (!m_pData && !aString)
		return 0;

	if (!m_pData)
		return -1;

	if (!aString)
		return 1;

	if (length == NPOS)
		return wcsicmp(m_pData + start, aString);
	else
		return wcsnicmp(m_pData + start, aString, length);
}

int32
String::caseCompare(int32 start, int32 length, const String & aString) const
{
	return caseCompare(start, length, (const uint16 *)aString);
}

int32
String::caseCompare(const uint16 * aString) const
{
	return caseCompare(0, NPOS, aString);
}

int32
String::caseCompare(const String & aString) const
{
	return caseCompare(0, NPOS, (const uint16 *)aString);
}

int32
String::compare(int32 start, int32 length, const uint16 * aString) const
{
	if (!m_pData && !aString)
		return 0;

	if (!m_pData)
		return -1;

	if (!aString)
		return 1;

	if (length == NPOS)
		return wcscmp(m_pData + start, aString);
	else
		return wcsncmp(m_pData + start, aString, length);
}

int32
String::compare(int32 start, int32 length, const String & aString) const
{
	return compare(start, length, (const uint16 *)aString);
}

int32
String::compare(const uint16 * aString) const
{
	return compare(0, NPOS, aString);
}

int32
String::compare(const String & aString) const
{
	return compare(0, NPOS, (const uint16 *)aString);
}


//////////////////////////////////////////////////////////////////////////////
/// @return         Number of bytes copied.
///
/// @param pRawTo	Data pointer to serialize to.
///
/// This method will serialize the string to a raw data pointer.
//////////////////////////////////////////////////////////////////////////////
uint32
String::serialize(void *pRawTo) const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

    // Convert raw pointer type
    uint8* pData = (uint8*)pRawTo;

    // Store the length as a 4 bytes.
    int32 byteLen = getLength();
	if (pData)
	{
		// FogBugz Case 1692 - casting to and int32 on an odd byte boundary normalizes
		// the pointer to an even byte boundary, the previous byte will get over-written.
		memcpy (pData, &byteLen, 4);
		pData += 4;

		// Write out all the bytes.  We are in little endian format, so write
		//  the lobyte first, then the high byte
		for( uint32 i = 0;i < getLength(); i++ )
		{
			*pData++ = LOBYTE( (*this)[i] );
			*pData++ = HIBYTE( (*this)[i] );
		}
	}

    // Return the number of bytes copied
	return ( (getLength() * sizeof(uint16)) + sizeof(byteLen) );
}

//////////////////////////////////////////////////////////////////////////////
/// @return         Number of bytes copied.
///
/// @param pRawTo	Data pointer to serialize to.
///
/// This method will convert to ansi and then serialize the string to a raw
///   data pointer.
//////////////////////////////////////////////////////////////////////////////
uint32
String::serializeAnsi(void *pRawTo) const
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

    // Convert raw pointer type
    uint8* pData = (uint8*)pRawTo;

	ASSERT(getLength() < 255)

	// Store the length as 1 byte.
    uint8 byteLen = (uint8)getLength();
	if (pData)
	{
		// FogBugz Case 1692 - casting to and int32 on an odd byte boundary normalizes
		// the pointer to an even byte boundary, the previous byte will get over-written.
		// for backwards compatibility we will only write 1 byte for the length.
		*pData++ = byteLen;

		// Write out all the bytes.  We are in little endian format, so write
		//  the lobyte first, then the high byte
		for( int32 i = 0; i < byteLen; i++ )
		{
			*pData++ = (uint8)(*this)[i];
		}
	}

    // Return the number of bytes copied
	return ( (byteLen * sizeof(uint8)) + 1 );
}

//////////////////////////////////////////////////////////////////////////////
/// @return         Number of bytes copied.
///
/// @param pRawFrom	Data pointer to serialize from.
///
/// This method will de-serialize the string from a raw data pointer.
//////////////////////////////////////////////////////////////////////////////
uint32
String::deserialize(void* pRawFrom)
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

    // Get the length
    int32 byteLen;

	memcpy (&byteLen, pRawFrom, 4);
	pRawFrom = (uint8*)pRawFrom + 4;

	if (byteLen == 0)
	{
		empty();
		return sizeof(byteLen);
	}

    // Allocate a new internal buffer (if needed) and copy the data
    uint32 nNewAllocSize = (uint32)byteLen + 1;
    if ( nNewAllocSize > m_nAllocated )
    {
        delete [] m_pData;
        m_nAllocated = nNewAllocSize;
        m_pData = new uint16[m_nAllocated];
    }

	if (m_pData)
	{
		uint16* pCharData = (uint16*)pRawFrom;
		wcsncpy( m_pData, pCharData, byteLen );

		m_pData[byteLen] = Character::null;

		// Update length variable
		m_nLength = byteLen;
		ASSERT( m_nLength == wcslen(m_pData) );

		return (m_nLength * sizeof(uint16)) + sizeof(byteLen);
	}
	else
	{
		m_nAllocated = 0;
		m_nLength = 0;

		return (byteLen * sizeof(uint16)) + sizeof(byteLen);
	}
}

//////////////////////////////////////////////////////////////////////////////
/// @return         Number of bytes copied.
///
/// @param pRawFrom	Data pointer to serialize from.
///
/// This method will de-serialize the string from a raw data pointer.  This
///  method assumes the incoming string is ANSI and will convert from ANSI
///  to unicode.
//////////////////////////////////////////////////////////////////////////////
uint32
String::deserializeAnsi(void* pRawFrom)
{
    // If GetBuffer() was called, don't allow operations until
    //   ReleaseBuffer() is called.
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

    // Get the length
    uint8* pLenData = (uint8*)pRawFrom;
    uint8 byteLen;

	// get the 1 byte length.  For backwards compatibility with the OLD stirings.
	byteLen = *pLenData++;

    // Allocate a new internal buffer (if needed) and copy the data
    uint32 nNewAllocSize = (uint32)byteLen + 1;
    if ( nNewAllocSize > m_nAllocated )
    {
        delete [] m_pData;
        m_nAllocated = nNewAllocSize;
        m_pData = new uint16[m_nAllocated];
    }

	if (m_pData)
	{
		for(uint32 i=0;i<(uint32)byteLen;i++)
		{
			m_pData[i] = pLenData[i];
		}

		m_pData[byteLen] = Character::null;
		m_pChar = m_pData;

		// Update length variable
		m_nLength = byteLen;
		ASSERT( m_nLength == wcslen(m_pData) );
	}
	else
	{
		m_nAllocated = 0;
		m_nLength = 0;
	}

	return byteLen + 1;
}

//////////////////////////////////////////////////////////////////////////////
/// @param szString		Pointer to the null-terminated string to copy from.
///
/// Internal helper routine, handles allocation of memory and copying from a
/// string.
//////////////////////////////////////////////////////////////////////////////
void
String::copyString(const uint16 * szString)
{
    ASSERT(!m_bLocked);

	if (!szString)
	{
		empty();
		return;
	}

	copyString(szString, wcslen(szString));
}


//////////////////////////////////////////////////////////////////////////////
/// @param szString		Pointer to the null-terminated string to copy from.
///
/// Internal helper routine, handles allocation of memory and copying from a
/// string.
//////////////////////////////////////////////////////////////////////////////
void
String::copyString(const uint16 * szString, uint32 nCharacters )
{
    ASSERT(!m_bLocked);

	if (!szString)
	{
		empty();
		return;
	}

	// See if need to allocate more space.
	if (nCharacters + 1 > m_nAllocated)
	{
		m_nAllocated = MAX(m_nLength * 2, nCharacters);

		// We do.  Clean out the old...
		m_nLength = 0;
		delete [] m_pData;

		// ...and bring in the new.
		m_pData = new uint16[ m_nAllocated + 1 ];
		if (m_pData)
			m_nAllocated = m_nAllocated + 1;
		else
			m_nAllocated = 0;
	}

	// Now, copy the string
	if (m_pData)
	{
		m_nLength = nCharacters;
		wcsncpy(m_pData, szString, m_nLength);
		m_pData[m_nLength] = Character::null;
		m_pChar = m_pData;
	}
}

//////////////////////////////////////////////////////////////////////////////
/// @param szString		Pointer to the null-terminated string to copy from.
///
/// Internal helper routine, handles allocation of memory and copying from a
/// string.
//////////////////////////////////////////////////////////////////////////////
void
String::copyString(const String & szString)
{
    ASSERT(!m_bLocked);
	ASSERT(!szString.m_bLocked);

	copyString((const uint16 *)szString, szString.m_nLength);
}

//////////////////////////////////////////////////////////////////////////////
/// @param szString		Pointer to the null-terminated string to copy from.
///
/// Internal helper routine, handles allocation of memory and copying from a
/// string.
//////////////////////////////////////////////////////////////////////////////
void
String::copyString(const String & szString, uint32 nCharacters )
{
    ASSERT(!m_bLocked);
	ASSERT(!szString.m_bLocked);

	copyString((const uint16 *)szString, nCharacters);
}

//////////////////////////////////////////////////////////////////////////////
/// @param szString		Pointer to the null-terminated string to copy from.
///
/// Internal helper routine, handles allocation of memory and copying from a
/// string.
//////////////////////////////////////////////////////////////////////////////
void
String::copyString(const char * szString)
{
    ASSERT(!m_bLocked);

	if (!szString)
	{
		empty();
		return;
	}

	copyString(szString, strlen(szString));
}

//////////////////////////////////////////////////////////////////////////////
/// @param szString		Pointer to the null-terminated string to copy from.
///
/// Internal helper routine, handles allocation of memory and copying from a
/// string.
//////////////////////////////////////////////////////////////////////////////
void
String::copyString(const char * szString, uint32 nCharacters )
{
    ASSERT(!m_bLocked);

	if (!szString)
	{
		empty();
		return;
	}

	// See if need to allocate more space.
	if (nCharacters + 1 > m_nAllocated)
	{
		m_nAllocated = MAX(m_nLength * 2, nCharacters);

		// We do.  Clean out the old...
		m_nLength = 0;
		delete [] m_pData;

		// ...and bring in the new.
		m_pData = new uint16[ m_nAllocated + 1 ];
		if (m_pData)
			m_nAllocated = m_nAllocated + 1;
		else
			m_nAllocated = 0;
	}

	// Now, copy the string
	if (m_pData)
	{
		m_nLength = nCharacters;
		StringConversions::convert(m_pData, szString, m_nLength + 1);
		m_pChar = m_pData;
	}
}

//////////////////////////////////////////////////////////////////////////////
/// @param szString		Pointer to the null-terminated string to concatenate
///                      to this one.
///
/// Internal helper routine, optimized concatenation since we already know
///   the length of both strings!
//////////////////////////////////////////////////////////////////////////////
void
String::concatenateString( const String& sString )
{
    ASSERT( !m_bLocked );

    // Get the new length of our string
    uint32 nNewLength = m_nLength + sString.getLength();

    // Note, we assume the correct amount of space has already been allocated!
    ASSERT( nNewLength <= m_nAllocated );

    // Manually copy the string since we know the starting point - strcat
    //  would walk the entire string again and we don't want that.
    for( uint32 i = 0; i < sString.getLength() + 1; i++ )
    {
        m_pData[m_nLength + i] = sString[i];
    }

    // Udate our internal length
    m_nLength = nNewLength;
}

//////////////////////////////////////////////////////////////////////////////
///
/// @return         Pointer to unicode string buffer.
///
/// @param len      Max length of buffer to be allocated and used.
///
/// This function allows direct access to the buffer of the string class.
///   PLEASE use extra care when using this function.  You must call
///   ReleaseBuffer() when you are done using the buffer directy and before
///   calling any other member functions.
//////////////////////////////////////////////////////////////////////////////
uint16*
String::getBuffer(const uint32 len)
{
    // Should not make multiple calls to GetBuffer without releasing the
    //  buffer first!
    if ( m_bLocked )
    {
        ASSERT(false);
        return 0;
    }

	if ( len > m_nAllocated )
    {
		m_nAllocated = len;
        uint16* pNewData = new uint16[m_nAllocated];
		if (pNewData)
		{
			if (m_pData)
			{
				wcscpy(pNewData, m_pData);
				delete [] m_pData;
			}
			else
			{
				*pNewData = '\0';
			}
		}
		else if (m_pData)
		{
			delete [] m_pData;
			m_pData = 0;

			m_nAllocated = 0;
			m_nLength = 0;
		}
		m_pData = pNewData;
    }

    // Set locked flag
    m_bLocked = true;

	return m_pData;
}

//////////////////////////////////////////////////////////////////////////////
/// Call this function after calling GetBuffer() to release control of the
///  internal buffer back to the string class.  If you call GetBuffer(), you
///  must call ReleaseBuffer() before using any other member functions.
//////////////////////////////////////////////////////////////////////////////
void
String::releaseBuffer()
{
    if ( m_bLocked )
    {
        // Have to recalculate the length now
        m_nLength = wcslen(m_pData);

        m_bLocked = false;
    }
    else
    {
        // Did you call GetBuffer first???
        ASSERT(false);
    }
}

//////////////////////////////////////////////////////////////////////////////
/// @return         Size of current string in bytes.
//////////////////////////////////////////////////////////////////////////////
uint32
String::getSize() const
{
    if ( m_bLocked )
    {
        return 0;
    }

	return (getLength() * sizeof(uint16));
}

//****************************************************************************
// Character Class
//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
/// Default Constructor
//////////////////////////////////////////////////////////////////////////////
String::Character::Character() : m_c(0)
{
}

//////////////////////////////////////////////////////////////////////////////
/// Construct from an ANSI character
//////////////////////////////////////////////////////////////////////////////
String::Character::Character( char c ) : m_c(c)
{
}

//////////////////////////////////////////////////////////////////////////////
/// Construct from an ANSI character
//////////////////////////////////////////////////////////////////////////////
String::Character::Character( uint8 c ) : m_c(c)
{
}

//////////////////////////////////////////////////////////////////////////////
/// Construct from an UNICODE character
//////////////////////////////////////////////////////////////////////////////
String::Character::Character( uint16 c ) : m_c(c)
{
}

//////////////////////////////////////////////////////////////////////////////
/// Copy Constructor
//////////////////////////////////////////////////////////////////////////////
String::Character::Character( const Character& c ) : m_c(c.m_c)
{
}

//////////////////////////////////////////////////////////////////////////////
/// Destructor
//////////////////////////////////////////////////////////////////////////////
String::Character::~Character()
{
}

//////////////////////////////////////////////////////////////////////////////
/// @return   Unicode character (uint16)
///
/// This is the cast operator to a unicode character.
//////////////////////////////////////////////////////////////////////////////
String::Character::operator uint16() const
{
	return m_c;
}

bool8
StringConversions::convert(uint16* to, const char* from, int32 size)
{
	int result = MultiByteToWideChar(
		CP_ACP,
		0,
		from,
		-1,
		to,
		size
		);

	return (bool8)(result & 0xFF);
}

bool8
StringConversions::convert(uint16* to, const uint16* from, int32 size)
{
	wcsncpy(to, from, size);
	
	return true;
}

bool8
StringConversions::convert(char* to, const char* from, int32 size)
{
	strncpy(to, from, size);

	return true;
}

bool8
StringConversions::convert(char* to, const uint16* from, int32 size)
{
	int result = WideCharToMultiByte(
		CP_ACP,
		0,
		from,
		-1,
		to,
		size,
		NULL,
		NULL
		);

	return (bool8)(result & 0xFF);
}

bool8
StringConversions::encodeUTF8(char* to, const uint16* from, int32 size)
{
	int result = WideCharToMultiByte(
		CP_UTF8,
		0,
		from,
		-1,
		to,
		size,
		NULL,
		NULL
		);

	return (bool8)(result & 0xFF);
}

bool8
StringConversions::decodeUTF8(uint16* to, const char* from, int32 size)
{
	int result = MultiByteToWideChar(
		CP_UTF8,
		0,
		from,
		-1,
		to,
		size
		);

	return (bool8)(result & 0xFF);
}
